/*
 * Copyright (c) 2025 CAX Conversion Project
 */
package com.comac.ins.isfd.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.comac.ins.common.core.constant.CommonConstant;
import com.comac.ins.common.core.exception.ServiceException;
import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.common.core.utils.MapstructUtils;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.common.mybatis.core.page.PageQuery;
import com.comac.ins.common.mybatis.core.page.TableDataInfo;
import com.comac.ins.common.oss.core.OssClient;
import com.comac.ins.common.oss.entity.UploadResult;
import com.comac.ins.common.oss.factory.OssFactory;
import com.comac.ins.isfd.domain.IsfdModelManagementRendering;
import com.comac.ins.isfd.domain.bo.IsfdModelManagementRenderingBo;
import com.comac.ins.isfd.domain.bo.query.IsfdModelManagementRenderingQueryBo;
import com.comac.ins.isfd.domain.vo.IsfdModelManagementPartVo;
import com.comac.ins.isfd.domain.vo.IsfdModelManagementRenderingResultVo;
import com.comac.ins.isfd.domain.vo.IsfdModelManagementRenderingVo;
import com.comac.ins.isfd.mapper.IsfdModelManagementRenderingMapper;
import com.comac.ins.isfd.service.IIsfdModelManagementPartService;
import com.comac.ins.isfd.service.IIsfdModelManagementRenderingService;
import com.comac.ins.system.service.ISysDictDataService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.services.s3.endpoints.internal.Value;

import java.io.*;
import java.util.Map;
import java.util.List;
import java.util.Collection;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;

import static com.comac.ins.isfd.util.CreateGltfFromBDF.createGltfFromBDF;

/**
 * 模型零件渲染Service业务层处理
 *
 * @author Lion Li
 * @date 2024-08-30
 */
@RequiredArgsConstructor
@Service
public class IsfdModelManagementRenderingServiceImpl implements IIsfdModelManagementRenderingService {

    @Autowired
    private final IsfdModelManagementRenderingMapper baseMapper;

    @Autowired
    protected IIsfdModelManagementPartService isfdModelManagementPartService;

    @Autowired
    private ISysDictDataService iSysDictDataService;

    /**
     * 查询模型零件渲染
     *
     * @param id 主键
     * @return 模型零件渲染
     */
    @Override
    public IsfdModelManagementRenderingVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询模型零件渲染列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 模型零件渲染分页列表
     */
    @Override
    public TableDataInfo<IsfdModelManagementRenderingVo> queryPageList(IsfdModelManagementRenderingBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<IsfdModelManagementRendering> lqw = buildQueryWrapper(bo);
        Page<IsfdModelManagementRenderingVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的模型零件渲染列表
     *
     * @param bo 查询条件
     * @return 模型零件渲染列表
     */
    @Override
    public List<IsfdModelManagementRenderingVo> queryList(IsfdModelManagementRenderingBo bo) {
        LambdaQueryWrapper<IsfdModelManagementRendering> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<IsfdModelManagementRendering> buildQueryWrapper(IsfdModelManagementRenderingBo bo) {
        LambdaQueryWrapper<IsfdModelManagementRendering> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getPartName()), IsfdModelManagementRendering::getPartName, bo.getPartName());
        lqw.eq(StringUtils.isNotBlank(bo.getParsedBdfPath()), IsfdModelManagementRendering::getParsedBdfPath, bo.getParsedBdfPath());
        lqw.eq(StringUtils.isNotBlank(bo.getGltfPath()), IsfdModelManagementRendering::getGltfPath, bo.getGltfPath());
        lqw.eq(StringUtils.isNotBlank(bo.getIndexPath()), IsfdModelManagementRendering::getIndexPath, bo.getIndexPath());
        lqw.eq(StringUtils.isNotBlank(bo.getMaterialLibraryPath()), IsfdModelManagementRendering::getMaterialLibraryPath, bo.getMaterialLibraryPath());
        lqw.eq(StringUtils.isNotBlank(bo.getCloudAddress()), IsfdModelManagementRendering::getCloudAddress, bo.getCloudAddress());
        lqw.eq(bo.getIsValid() != null, IsfdModelManagementRendering::getIsValid, bo.getIsValid());
        return lqw;
    }

    /**
     * 查询唯一符合条件的模型零件渲染数据
     *
     * @param businessId, type 查询条件
     * @return 模型零件渲染单条数据
     */
    @Override
    public IsfdModelManagementRenderingVo queryUniqueOne(Long businessId, String type) {
        return baseMapper.selectVoOne(new LambdaQueryWrapper<IsfdModelManagementRendering>()
            .eq(businessId != null, IsfdModelManagementRendering::getBusinessId, businessId)
            .eq(StringUtils.isNotBlank(type), IsfdModelManagementRendering::getType, type));
    }

    /**
     * 新增模型零件渲染
     *
     * @param bo 模型零件渲染
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(IsfdModelManagementRenderingBo bo) {
        return Boolean.TRUE;
    }

    /**
     * 加载模型零件渲染
     *
     * @param bo 模型零件渲染
     * @return gltf文件及xref文件
     */
    @Override
    public IsfdModelManagementRenderingResultVo load(IsfdModelManagementRenderingQueryBo bo) {
        // 根据零件/任务id查询文件位置
        Long businessId = bo.getBusinessId();
        String type = bo.getType();
        String partName = "";
        IsfdModelManagementPartVo isfdModelManagementPartVo = isfdModelManagementPartService.queryById(businessId);
        IsfdModelManagementRenderingVo isfdModelManagementRenderingVo = this.queryUniqueOne(businessId, type);

        if (isfdModelManagementPartVo == null) {
            // 如果零件库内没有，但渲染库内查到，删掉
            if (isfdModelManagementRenderingVo != null) {
                baseMapper.deleteById(isfdModelManagementRenderingVo.getId());
            }
            throw new BaseException("零件不存在，零件号：" + businessId + "，没有查询到相关数据，请确认数据后重试！");
        }

        // 获取文件保存路径前缀
        String bdfPrefix = iSysDictDataService.selectDictValueByTypeAndLabel(CommonConstant.SYS_COMMON_DICT_TYPE,
            CommonConstant.MODEL_MANAGEMENT_PART_DATA_PREFIX);
        String partRenderPrefix = iSysDictDataService.selectDictValueByTypeAndLabel(CommonConstant.SYS_COMMON_DICT_TYPE,
            CommonConstant.MODEL_MANAGEMENT_PARTR_ENDER_PREFIX);
        if (StringUtils.isBlank(partRenderPrefix)||StringUtils.isBlank(bdfPrefix)){
            throw new BaseException("字典中没有找到文件存储地址的相关配置。");
        }

        if (isfdModelManagementRenderingVo == null){
            // 生成文件
            String[] paths = generateFiles(isfdModelManagementPartVo, partRenderPrefix, bdfPrefix);

            // 新增落表
            partName = isfdModelManagementPartVo.getPartName();
            IsfdModelManagementRendering isfdModelManagementRendering = new IsfdModelManagementRendering();
            isfdModelManagementRendering.setBusinessId(businessId);
            isfdModelManagementRendering.setType(type);
            isfdModelManagementRendering.setPartName(partName);
            isfdModelManagementRendering.setRenderingPath(partRenderPrefix);
            isfdModelManagementRendering.setParsedBdfPath(paths[0]);
            isfdModelManagementRendering.setGltfPath(paths[1]);
            isfdModelManagementRendering.setItemXrefPath(paths[2]);
            isfdModelManagementRendering.setMaterialLibraryPath(paths[3]);
            isfdModelManagementRendering.setIndexPath(paths[4]);
            baseMapper.insert(isfdModelManagementRendering);

            isfdModelManagementRenderingVo = this.queryUniqueOne(businessId, type);
        }

        // 如果vo的渲染文件存储路径与现在的不一样，也重新生成文件
        String renderingPath = isfdModelManagementRenderingVo.getRenderingPath();
        if (partRenderPrefix.equals(renderingPath) == Boolean.FALSE){
            // 生成文件
            String[] paths = generateFiles(isfdModelManagementPartVo, partRenderPrefix, bdfPrefix);

            // 更新vo
            isfdModelManagementRenderingVo.setRenderingPath(partRenderPrefix);
            isfdModelManagementRenderingVo.setParsedBdfPath(paths[0]);
            isfdModelManagementRenderingVo.setGltfPath(paths[1]);
            isfdModelManagementRenderingVo.setItemXrefPath(paths[2]);
            isfdModelManagementRenderingVo.setMaterialLibraryPath(paths[3]);
            isfdModelManagementRenderingVo.setIndexPath(paths[4]);
            IsfdModelManagementRendering isfdModelManagementRendering = MapstructUtils.convert(isfdModelManagementRenderingVo, IsfdModelManagementRendering.class);
            baseMapper.updateById(isfdModelManagementRendering);
        }

        // 获取vo里的存储路径
        String gltfPath = isfdModelManagementRenderingVo.getGltfPath();
        String xrefPath = isfdModelManagementRenderingVo.getItemXrefPath();
        String binPath = this.replaceExtension(gltfPath, ".bin");

        // 如果文件为空，也重新生成
        if (new File(xrefPath).exists() == Boolean.FALSE){
            generateFiles(isfdModelManagementPartVo, partRenderPrefix, bdfPrefix);
        }

        // 返回vo实体
        IsfdModelManagementRenderingResultVo isfdModelManagementRenderingResultVo = new IsfdModelManagementRenderingResultVo();
        isfdModelManagementRenderingResultVo.setGltfPath(gltfPath);
        isfdModelManagementRenderingResultVo.setXrefPath(xrefPath);
        isfdModelManagementRenderingResultVo.setBinPath(binPath);

        return isfdModelManagementRenderingResultVo;
    }

    private String[] generateFiles(IsfdModelManagementPartVo isfdModelManagementPartVo,
                               String partRenderPrefix,
                               String bdfPrefix){
        // 新增零件渲染数据
        String materialPath = iSysDictDataService.selectDictValueByTypeAndLabel(CommonConstant.SYS_COMMON_DICT_TYPE,
            CommonConstant.MODEL_MANAGEMENT_MATERIAL_PATH);

        // 批次号
        String batchNumber = isfdModelManagementPartVo.getBatchNumber();

        // 获取零件及材料bdf文件路径
        String bdfPath = batchNumber + File.separator + isfdModelManagementPartVo.getIndexPath();

        // 根据bdf文件路径生成gltf，xref文件路径
        String parsedBdfPath = this.replaceExtension(bdfPath, "_parsed_bdf.json");
        String gltfPath = this.replaceExtension(bdfPath, ".gltf");
        String xrefPath = this.replaceExtension(bdfPath, ".json");

        // 获取完整路径
        String bdfFullPath = bdfPrefix + bdfPath;
        String materialFullPath = materialPath;
        String gltfFullPath = partRenderPrefix + gltfPath;
        String xrefFullPath = partRenderPrefix + xrefPath;
        String parsedBdfFullPath = partRenderPrefix + parsedBdfPath;

        // 解析bdf
        Map parsingResult = createGltfFromBDF(bdfFullPath, materialFullPath, parsedBdfFullPath, gltfFullPath, xrefFullPath);

        return new String[]{parsedBdfFullPath, gltfFullPath, xrefFullPath, materialFullPath, bdfFullPath};
    }

    private String replaceExtension(String filePath, String newExtension) {
        int dotIndex = filePath.lastIndexOf('.');
        if (dotIndex != -1) {
            return filePath.substring(0, dotIndex) + newExtension;
        } else {
            return filePath + newExtension;
        }
    }


    /**
     * 根据文件路径列表查找文件，并将这些文件打包为一个压缩文件
     *
     * @param filePaths 要查找的文件路径列表
     * @return 压缩文件的字节数组
     * @throws IOException 如果在文件操作或压缩过程中发生错误
     */
    public byte[] findAndZipFiles(List<String> filePaths) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        boolean filesFound = false;

        try (ZipOutputStream zos = new ZipOutputStream(bos)) {
            for (String path : filePaths) {
                File file = new File(path);
                if (file.exists() && file.isFile()) {
                    addToZipFile(file, zos);
                    filesFound = true;
                } else {
                    System.err.println("File not found: " + path);
                }
            }
            if (!filesFound) {
                throw new FileNotFoundException("No files found to zip.");
            }
            zos.finish();
        }

        return bos.toByteArray();
    }

    /**
     * 将单个文件添加到压缩包中
     *
     * @param file 要添加的文件
     * @param zos  ZipOutputStream实例
     * @throws IOException 如果在文件读取或压缩过程中发生错误
     */
    private void addToZipFile(File file, ZipOutputStream zos) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            ZipEntry zipEntry = new ZipEntry(file.getName());
            zos.putNextEntry(zipEntry);

            byte[] bytes = new byte[1024];
            int length;
            while ((length = fis.read(bytes)) >= 0) {
                zos.write(bytes, 0, length);
            }

            zos.closeEntry();
        }
    }

    /**
     * 修改模型零件渲染
     *
     * @param bo 模型零件渲染
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(IsfdModelManagementRenderingBo bo) {
        IsfdModelManagementRendering update = MapstructUtils.convert(bo, IsfdModelManagementRendering.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(IsfdModelManagementRendering entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除模型零件渲染信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }
}
